//
//  File.swift
//
//
//  Created by didi on 2022/9/8.
//

import MolecularObjects
import Vapor

struct SystemRouter: MolecularRouter {
    let responseController = SystemResponseController()

    let permissionApi = SystemPermissionApiController()
    let variableApi = SystemVariableApiController()
    let metadataApi = SystemMetadataApiController()
    let fileApi = SystemFileApiController()
    let adminDashboard = SystemAdminDashboardController()
    let permissionAdmin = SystemPermissionAdminController()
    let variableAdmin = SystemVariableAdminController()
    let metadataAdmin = SystemMetadataAdminController()
    let fileAdmin = SystemFileAdminController()

    func boot(_ app: Application) throws {
        app.routes.get(app.molecular.config.paths.manifest.pathComponent, use: responseController.renderManifestFile)
    }

    func config(_ app: Application) throws {
        let middlewares: [Middleware] = app.invokeAllFlat(.middlewares)
        let routes = app.routes
            .grouped(SystemInstallGuardMiddleware())
            .grouped(middlewares)

        var arguments = HookArguments()
        arguments.routes = routes
        let _: [Void] = app.invokeAll(.routes, args: arguments)
    }

    func routesHook(_ args: HookArguments) {
        // MARK: - public api

        let publicApiMiddlewares: [Middleware] = args.app.invokeAllFlat(.publicApiMiddlewares)
        let publicApiRouters = args.routes
            .grouped(args.app.molecular.config.paths.api.pathComponent)
            .grouped(SystemApiErrorMiddleware())
            .grouped(publicApiMiddlewares)

        publicApiRouters.get("status") { _ in "ok" }
        publicApiRouters.get(.catchall) { _ -> Response in throw Abort(.notFound) }
        publicApiRouters.post(.catchall) { _ -> Response in throw Abort(.notFound) }
        publicApiRouters.put(.catchall) { _ -> Response in throw Abort(.notFound) }
        publicApiRouters.patch(.catchall) { _ -> Response in throw Abort(.notFound) }
        publicApiRouters.delete(.catchall) { _ -> Response in throw Abort(.notFound) }

        var publicApiArguments = HookArguments()
        publicApiArguments.routes = publicApiRouters
        let _: [Void] = args.app.invokeAll(.publicApiRoutes, args: publicApiArguments)

        // MARK: - api

        let apiMiddlewares: [Middleware] = args.app.invokeAllFlat(.apiMiddlewares)
        let apiRouters = publicApiRouters
            .grouped(apiMiddlewares)

        permissionApi.setUpRoutes(apiRouters)
        variableApi.setUpRoutes(apiRouters)
        metadataApi.setUpListRoutes(apiRouters)
        metadataApi.setUpDetailRoutes(apiRouters)
        metadataApi.setUpUpdateRoutes(apiRouters)
        metadataApi.setUpPatchRoutes(apiRouters)
        fileApi.setUpRoutes(apiRouters)

        var apiArguments = HookArguments()
        apiArguments.routes = apiRouters
        let _: [Void] = args.app.invokeAll(.apiRoutes, args: apiArguments)

        // MARK: - web

        let webMiddlewares: [Middleware] = args.app.invokeAllFlat(.webMiddlewares)
        let webRoutes = args.routes
            .grouped(MolecularMenuMiddleware())
            .grouped(webMiddlewares)
            .grouped(SystemErrorMiddleware())

        var webArguments = HookArguments()
        webArguments.routes = webRoutes
        let _: [Void] = args.app.invokeAll(.webRoutes, args: webArguments)

        // MARK: - admin

        let adminMiddlewares: [Middleware] = args.app.invokeAllFlat(.adminMiddlewares)
        let adminRoutes = args.routes
            .grouped(args.app.molecular.config.paths.admin.pathComponent)
            .grouped(MolecularMenuMiddleware())
            .grouped(adminMiddlewares)
            .grouped([
                AccessRedirectMiddleware(MolecularObjects.System.permission(for: .detail), path: "/"),
                SystemAdminErrorMiddleware(),
            ])

        adminRoutes.get(use: adminDashboard.renderDashboardTemplate)
        adminRoutes.get(MolecularObjects.System.pathKey.pathComponent) { request -> Response in
            request.templates.renderHtml(SystemAdminModulePageTemplate(.init(title: "System",
                                                                             tag: SystemAdminWidgetTemplate().render(request))))
        }
        adminRoutes.get(.catchall) { _ -> Response in throw Abort(.notFound) }
        adminRoutes.post(.catchall) { _ -> Response in throw Abort(.notFound) }

        permissionAdmin.setUpRoutes(adminRoutes)
        variableAdmin.setUpRoutes(adminRoutes)
        metadataAdmin.setUpListRoutes(adminRoutes)
        metadataAdmin.setUpDetailRoutes(adminRoutes)
        metadataAdmin.setUpUpdateRoutes(adminRoutes)
        fileAdmin.setUpRoutes(adminRoutes)

        var adminArguments = HookArguments()
        adminArguments.routes = adminRoutes
        let _: [Void] = args.app.invokeAll(.adminRoutes, args: adminArguments)

        // MARK: - public web

        webRoutes.get(use: responseController.handle)
        webRoutes.get(.catchall, use: responseController.handle)
        webRoutes.post(.catchall, use: responseController.handle)
    }
}
